﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
using Ymatou.Counter.Client40;
using Ymatou.User.Dto.Auth;
using YmtAuth.AppService.Safe;
using YmtAuth.Common.Extend;
using YmtAuth.Domain.EventHistory;
using YmtAuth.Domain.Model.BlacklistUser;
using YmtAuth.Domain.Model.LoginAuth;
using YmtAuth.Domain.Model.Safe;
using YmtAuth.Domain.Model.YmatouUserBills.TencentLoginProtection;
using YmtAuth.Domain.Repository;
using YmtAuth.Domain.Shard;
using YmtAuth.Dto;
using YmtAuth.LogService;
using YmtAuth.Repository.MSSQL;
using YmtSystem.CrossCutting;
using YmtSystem.Infrastructure.EventBusService;
using ServiceStack;
using ServiceStack.Web;
using YmtAuth.Common.Cache;
using YmtAuth.Common.Utility;
using YmtAuth.Domain.DomainEvent;
using YmtAuth.AppService.ThirdPartyService;
using YmtAuth.AppService.Model;
using YmtAuth.Repository.MySQL;
using YmtAuth.Common;
using YmatouMessageBusClientNet4;
using YmtAuth.AppService.JavaService;
using YmtAuth.Dto.UserLoginMessage;

namespace YmtAuth.AppService
{
    public class AuthAppService : IAuthAppService
    {
       private ISecurityQuestionAppService securityQuestionAppService = new SecurityQuestionAppService();

        internal static LocalCache2<int, byte> UserTypeCache = new LocalCache2<int, byte>();  
        private static readonly IUserLoginBillsRepository UlcRepo =LocalServiceLocator.GetService<IUserLoginBillsRepository>();
        private static readonly ITokenAppService tokenApp = new TokenAppService();
        private static readonly IYmtUserLoginHistoryRepository LoginHistoryRepo = LocalServiceLocator.GetService<IYmtUserLoginHistoryRepository>();
        private static readonly UserDeviceAuthRepository deviceRepo = new UserDeviceAuthRepository();

        private const string innerClientSource = "userservice.ymatou.com";

        private static readonly IDualSecurityAppService dualSecurityApp = new DualSecurityAppService();

        private static readonly bool EnableJavaLoginAuth = string.Equals(ConfigurationManager.AppSettings["EnableJavaLoginAuth"], "1");

        /// <summary>
        /// 登录认证
        /// </summary>      
        public ResponseData<LoginAuthResponseDto2> LoginAuth2(LoginAuthRequestDto dto, IRequest request = null)
        {
            string requestId = HttpHelper.GetHeaderParameter(YmtUserKey.RequestId);
            YmatouLoggingService.Debug("[BeginLogin] LoginAuth2, requestId: {0}, parameters: {1}", requestId,
                dto.ToString());
            //1.参数检查
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的LoginAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.LoginId, "无效的LoginId");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Pas, "无效的Pas");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.ClientIp, "无效客户端IP");

            string reqDomain = dto.SourceType;
                                 
            //如果是用户服务请求，则直接执行 InternalServiceAuth
            if (!dto.SourceType.IsEmpty() && dto.SourceType.ToLower() == innerClientSource)
            {
                YmatouLoggingService.Debug("InternalServiceAuth, requestId: {0}", requestId);
                return InternalServiceAuth(dto);
            }

            if (request != null)
            {
                if (RequestUserService.isSendReadToJavaUserService(dto, "LoginAuthRequestDto", "GET",
                    new string[] {dto.LoginId}))
                {
                    var result = RequestUserService.sendReadRequest<ResponseData<LoginAuthResponseDto2>>(dto, "LoginAuthRequestDto", request.RawUrl, "GET",
                        request.QueryString);
                    if (result.isRequestSuccess) return result.resp;
                }
            }
            //2 登录ID格式检查
            var checkLoginIdResult = UserLoginBill.CheckLoginId(dto.LoginId);
            if (!checkLoginIdResult.Success)
            {
                WriteLoginLogAsync(dto, 0, 0, checkLoginIdResult.Result.Code.GetDescription(),
                    AuthSafeHandleCode.LoginIdFormartError, authType: checkLoginIdResult.Result.Code.ToString());
                return checkLoginIdResult;
            }
            //检查是否禁用买手油箱登陆
            if (UserLoginAuthSpecifications.CheckSellerLoginDisableEmail(dto.LoginId, dto.SourceType))
            {
                WriteLoginLogAsync(dto, 0, 0, "买手邮箱被禁止登陆", AuthSafeHandleCode.DisableSellerMailLogin,
                    authType: AuthSafeHandleCode.DisableSellerMailLogin.ToString());
                return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                {
                    UserData = new UserLoginBills(),
                    Code = LoginResponseCode.InvalidLoingId
                }, "-410", UserLoginAuthSpecifications.GetDisableSellerEmailResMessage());
            }
            //如果APP请求，clientID，DeviceId都为空则直接返回错误
            if (!dto.SourceType.IsEmpty() && dto.SourceType.ToLower() == "app"
                && ConfigurationManager.AppSettings["CheckAppRequestDeviceId"] == "1"
                && (dto.ClientId.IsEmpty() && dto.DeviceId.IsEmpty()))
            {
                YmatouLoggingService.Debug(
                    "APP请求，clientID，DeviceId都为空则直接返回错误； source: {0}, login id: {1}, requestId: {2}", dto.SourceType,
                    dto.LoginId, requestId);
                WriteLoginLogAsync(dto, 0, 0, "APP请求，clientID，DeviceId都为空返回错误", AuthSafeHandleCode.AppFormartError,
                    authType: "AppFormartError");
                return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                {
                    UserData = new UserLoginBills(),
                    Code = LoginResponseCode.RequestParameterError
                }, "50", "clientid,devcieid 不能为空");
            }

            //如果是走老接口的，改变终端类型强制走pc的ip域的风控
            if (string.IsNullOrEmpty(dto.SourceType) || dto.SourceType.ToLower() == "app")
            {
                if (string.IsNullOrEmpty(dto.DeviceId) && string.IsNullOrEmpty(dto.ClientId))
                {
                    YmatouLoggingService.Debug(
                        "如果是走老接口的，改变终端类型强制走pc的ip域的风控, LoginBlackIP； source: {0}, login id: {1}, requestId: {2}",
                        dto.SourceType, dto.LoginId, requestId);
                    dto.SourceType = "pc";
                }
            }

            var watch = Stopwatch.StartNew();
            UserLoginBill ulc = null;
            //检测用户白名单
            ResponseData<UserLoginBill> isWhiteUserRes = WhiteListAppService.IsWhiteUser(dto);

            //白名单用户只验密码
            if (isWhiteUserRes.Success)
            {
                ulc = isWhiteUserRes.Result;

                var checkResult = ulc.CheckUserAllowLogin(dto.Pas, dto.LoginId);
                if (!checkResult.Success)
                {
                    YmatouLoggingService.Debug("[CheckUserAllowLogin],requestId: {0}, login id: {1}, clientIp: {2}, source: {3}, deviceId: {4}", requestId, dto.LoginId, dto.ClientIp, dto.SourceType, dto.DeviceId);
                    BlacklistUserEvent(dto);
                    WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc.UserId, "登录密码错误", AuthSafeHandleCode.LoginPasswordError, authType: "LoginPasswordError");
                    BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                    BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId, LoginHistoryRepo);
                    return checkResult;
                }

                //收集用户常用IP区域，设备号
                UserSafeVerify.AddUserLoginDeviceAsync(dto.DeviceId, dto.ClientId, ulc.UserId, dto.LoginId, dto.SourceType, dto.ClientIp);
                UserSafeVerify.AddUserLoginIpAreaAsync(dto.ClientIp, ulc.UserId, dto.SourceType);
            }
            else
            {
                // 增加IP黑名单的校验；Modified by Wesley on 20160627
                bool isBlackIP = CheckIpBlackList(dto.ClientIp, dto.SourceType);
                if (isBlackIP)
                {
                    YmatouLoggingService.Debug("黑名单IP，禁止登录, LoginBlackIP； clientIp: {0}, source: {1}, login id: {2}, deviceId: {3}, requestId: {4}",
                        dto.ClientIp, dto.SourceType, dto.LoginId, dto.DeviceId, requestId);
                    WriteLoginLogAsync(dto, 0, 0, "黑名单IP，禁止登录", AuthSafeHandleCode.LoginBlackIP, false, authType: "BlackIP");
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2 { UserData = new UserLoginBills(), Code = LoginResponseCode.Limit },
                            "-400", "登录频繁请休息会");
                }

                //3.BlacklistUser-用户登陆ID是否是黑名单
                YmatouLoggingService.Debug("判断用户登陆ID是否是黑名单 requestId: {0}, LoginId: {1}", requestId, dto.LoginId);
                var checkIsBlackUser = UserStatesAppService.CheckBlacklistUser(dto.LoginId);
                if (checkIsBlackUser.Item1 != UserStates.Normal)
                {
                    YmatouLoggingService.Debug("黑名单登陆ID，禁止登录, LoginUserBlackList,LoginId :{0}, requestId: {1}", dto.LoginId,
                        requestId);
                    WriteLoginLogAsync(dto, 0, 0, "黑名单登陆ID，禁止登录", AuthSafeHandleCode.LoginUserBlackList,
                        authType: "BlackListUser");
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                    {
                        UserData = new UserLoginBills(),
                        Code = LoginResponseCode.UserLocked
                    }, "-410", checkIsBlackUser.Item2);
                }

                //用户ID是否超过登陆限制               
                if (CheckUserLoginCountGTLimit(dto.LoginId))
                {
                    YmatouLoggingService.Debug("{0} 用户 {1} 登录ID超过登录限制,client ip :{2}, requestId: {3}", dto.LoginId,
                        dto.SourceType, dto.ClientIp, requestId);
                    WriteLoginLogAsync(dto, 0, 0, "登录ID超过登录限制", AuthSafeHandleCode.LoginCountGTLimit,
                        authType: "LoginCountGTLimit");
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                    {
                        UserData = new UserLoginBills(),
                        Code = LoginResponseCode.Limit
                    }, "1", "登录频繁请休息会");
                }

                //4.query
                var query = UserLoginAuthSpecifications.MatchLoginKey(dto.LoginId);
                ulc = UlcRepo.FindOne(query, true);
                //5.async add log
                //WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc);
                //6.async add blacklist loingid & ip             
                //7.check 
                if (ulc == null)
                {
                    YmatouLoggingService.Debug("用户ID不存在: requestId: {0}, LoginId: {1}", requestId, dto.LoginId);
                    WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, 0, "用户ID不存在: " + dto.LoginId, AuthSafeHandleCode.LoginIdError, authType: "LoginIdFail");
                    BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                    BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId, LoginHistoryRepo);
                    YmatouLoggingService.Debug("login id error，query: {0},source: {1},clientIp: {2}", query.ToString(), dto.SourceType, dto.ClientIp);
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2 { UserData = new UserLoginBills(), Code = LoginResponseCode.LoingIdOrPasError }, "0", "用户名或密码错误");
                }

                var checkResult = ulc.CheckUserAllowLogin(dto.Pas, dto.LoginId);
                if (!checkResult.Success)
                {
                    YmatouLoggingService.Debug("[CheckUserAllowLogin],requestId: {0}, login id: {1}, clientIp: {2}, source: {3}, deviceId: {4}", requestId, dto.LoginId, dto.ClientIp, dto.SourceType, dto.DeviceId);
                    BlacklistUserEvent(dto);
                    WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc.UserId, "登录密码错误", AuthSafeHandleCode.LoginPasswordError, authType: "LoginPasswordError");
                    BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                    BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId, LoginHistoryRepo);
                    return checkResult;
                }

                //检查买手登录来源Ip是否被禁用 
                if (CheckSellerDenyLogin(dto.ClientIp, ulc.UserId, dto.SourceType))
                {
                    BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                    BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId, LoginHistoryRepo);

                    WriteLoginLogAsync(dto, 0, ulc.UserId, "买手本地局域网IP，执行身份认证", AuthSafeHandleSubCode.NeedIdentityVerify_LanIP, false, "NeedIdentityVerify");

                    //TODO 发布登录密码错误次数事件
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = ulc.UserId,
                        AppSource = dto.SourceType,
                        AuthType = "SellerLanIPVerify",
                        ResponseCode = AuthSafeHandleSubCode.NeedIdentityVerify_LanIP,
                        LoginId = dto.LoginId,
                        LoginIp = dto.ClientIp,
                        Desc = "买手本地局域网IP，执行身份认证",
                        DeviceId = dto.DeviceId,
                        ClientId = dto.ClientId,
                        RunTime = 0
                    });

                    var code = ConfigurationManager.AppSettings["EnableNeedIdentityVerifyTips"] == "1"
                           ? LoginResponseCode.NeedIdentityVerify
                           : LoginResponseCode.UserLocked;
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                    {
                        UserData = new UserLoginBills { LoginMail = ulc.LoginBills.Email, LoginMobile = ulc.LoginBills.Mobile },
                        Code = code
                    }, "100", "请执行身份验证");
                }

                int userId = ulc.UserId;

                //买家iphone7 免安全认证 http://wiki.ymatou.cn:8090/pages/viewpage.action?pageId=21007113
                bool isIphone7UserLogined = iphone7UserLogin(requestId, userId, dto.SourceType, dto.DeviceId);

                //收集用户常用IP区域，设备号
                UserSafeVerify.AddUserLoginDeviceAsync(dto.DeviceId, dto.ClientId, ulc.UserId, dto.LoginId, dto.SourceType, dto.ClientIp);
                UserSafeVerify.AddUserLoginIpAreaAsync(dto.ClientIp, ulc.UserId, dto.SourceType);

                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //安全检查 根据配置判断是否走安全流程检查 
                if (!isIphone7UserLogined && IsCheckLoginSafe(dto.SourceType, ulc.UserId, dto.TSource, dto.AppTypeChanndel))
                {
                    //执行安全认证流程
                    AuthSafeHandleCode safeCode = null;

                    //按百分比导流量到java用户安全
                    bool spreadJavaLoginAuth = ((ulc.UserId % 10) + 1) <= ConfigUtility.SpreadJavaLoginAuth;

                    if (spreadJavaLoginAuth)
                    {
                        //获取java用户安全返回的结果
                       var userSafeResult =  UserSafeService.LoginAuthExecute(ulc.UserId, dto.LoginId, dto.SourceType, reqDomain
                            , dto.DeviceId, dto.ClientId, dto.ClientIp);
                        if (userSafeResult.Result != null)
                        {
                            YmatouLoggingService.Debug(
                                "Java usersafe service response,code:{0},authcode:{1},authMsg:{2} "
                                , userSafeResult.ErrorCode, userSafeResult.Result.Code,
                                userSafeResult.Result.AuthMessage);
                            safeCode = AuthSafeHandleCode.CreateCode(userSafeResult.Result.Code,
                                message: userSafeResult.Result.AuthMessage);
                            var ipArea = "";
                            //来源为买手且IP区域未验证则记录IP区域
                            if (IsSellerUser(ulc.UserId, dto.SourceType)
                                && (userSafeResult.Result.Code == 155
                                    || userSafeResult.Result.Code == 101))
                            {
                                var ipAreaInfo = UserSafeVerify.FindUserIpArea(dto.ClientIp);
                                if (ipAreaInfo != null)
                                {
                                    ipArea = "{0}|{1}|{2}".Fmt(ipAreaInfo.Country, ipAreaInfo.Province, ipAreaInfo.City);
                                }
                            }
                            EventBus.Instance.Publish(new LoginSafeVerifyEvent
                            {
                                UserId = ulc.UserId,
                                AppSource = dto.SourceType,
                                AuthType = "javaSafeverify",
                                ResponseCode = safeCode.SubCode,
                                IpArea = ipArea,
                                LoginId = dto.LoginId,
                                LoginIp = dto.ClientIp,
                                Desc = string.Format("{0}", userSafeResult.Result.AuthMessage),
                                RunTime = watch.Elapsed.TotalMilliseconds
                            });
                        }
                        else
                        {
                            safeCode = UserSafeVerify.LoginAuthSafeHandleExecute(dto.LoginId, ulc.UserId, dto.ClientIp,
                            dto.SourceType, dto.DeviceId, dto.ClientId);
                            YmatouLoggingService.Debug(
                                "Java userfave service request error,use NET login safe,loginId:{0}",dto.LoginId);
                        }
                    }
                    else
                    {
                        safeCode = UserSafeVerify.LoginAuthSafeHandleExecute(dto.LoginId, ulc.UserId, dto.ClientIp,
                        dto.SourceType, dto.DeviceId, dto.ClientId);
                    }

                    if (safeCode.Code == AuthSafeHandleCode.NeedIdentityVerify
                        || (spreadJavaLoginAuth &&safeCode.Code!=200))//java 返回 非200表示被风控
                    {
                        WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc.UserId,
                            "NeedIdentityVerify:" + safeCode.Message, safeCode.SubCode, authType: "NeedIdentityVerify");
                        BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                        BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId,
                            LoginHistoryRepo);
                        var code = ConfigurationManager.AppSettings["EnableNeedIdentityVerifyTips"] == "1"
                            ? LoginResponseCode.NeedIdentityVerify
                            : LoginResponseCode.UserLocked;

                        UserLoginBills userLoginBills = new UserLoginBills { LoginMail = ulc.LoginBills.Email, LoginMobile = ulc.LoginBills.Mobile };
                        //买家登录被风控，需要返回UserId，用于获取安全问题
                        if (IsBuyerUser(userId, dto.SourceType))
                        {
                            userLoginBills.UserId = userId;
                        }
                        //2016.12.1 增加  LoginRiskCode
                        //var respCode = "100";
                        return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                        {
                            UserData = userLoginBills,
                            Code = code,
                            LoginRiskCode = safeCode.SubCode
                        }, "100", safeCode.ResponseMessage ?? "请执行身份验证");
                    }
                    if (safeCode.Code == AuthSafeHandleCode.DenyLogin)
                    {
                        WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc.UserId, "DenyLogin:" + safeCode.Message,
                            AuthSafeHandleCode.DenyLogin, authType: "DenyLogin");
                        BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, false);
                        BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId,
                            LoginHistoryRepo);
                        var tipMsg =
                            SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                                .TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("Tips"));
                        return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2
                        {
                            UserData = new UserLoginBills(),
                            Code = LoginResponseCode.UserLocked
                        }, safeCode.Code.ToString(), tipMsg ?? "拒绝登录");
                    }
                }
            }
            
            watch.Stop();

            //8.update login time          
            WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, ulc.UserId, "success",
                code:AuthSafeHandleCode.LoginSuccess, authType: "success");
           BlackListAppService.TryAddBlackListIpAsync(dto.SourceType, dto.ClientIp, dto.LoginId, true);
           BlackListAppService.TryAddBlackListDeviceAsync(dto.SourceType, dto.DeviceId, dto.ClientId, LoginHistoryRepo);
           UpdateLoginTimeAsync(dto, ulc);

            //9.add token
            var response = tokenApp.CreateUserToken(new UserTokenDto
            {
                UserId = ulc.UserId,
                TokenTimeOut = (long?) dto.SSOTokenTimeOut,
                TokenSource = dto.TokenSource
            });
            //                      
            YmatouLoggingService.Debug(
                "[LoginEnd] requestId: {0}, login end; LoginId {1} login end, run time {2} ms, Token: {3}", requestId,
                dto.LoginId, watch.Elapsed.TotalMilliseconds, response.Result);

            //只有买手才记录用户的在线状态
            SellerOnlineServiceAgent.PostSellerOnlineLoginStatus(new SellerOnlineStatusOp()
            {
                AppSource = dto.SourceType,
                UserId = ulc.UserId,
                OpType = SellerOnlineOpType.AppLoginStatus_OpType,
                Op =  "1",// 1表示登入
                OpTime = DateTime.Now
            });

            ///更新设备号信息
            DeviceIdService.UpdateDeviceinfo(ulc.UserId, dto.DeviceId);
            //登录成功抛出消息
            if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["EnableLoginPostMessage"]) ||
                ConfigurationManager.AppSettings["EnableLoginPostMessage"] == "1")
            {
                MessageBusAgent.PublishAsync("ymatouuser", "login",
                    string.Format("{0}", ulc.UserId), new
                        LoginMessageDto
                    {
                        UserId = ulc.UserId,
                        Mobile = ulc.LoginBills.Mobile,
                        LoginTime = DateTime.Now,
                        DeviceId = dto.DeviceId,
                        TerminalSource = dto.TSource
                    });
            }
            //10.return           
            return ResponseData<LoginAuthResponseDto2>.CreateSuccess(new LoginAuthResponseDto2
            {
                UserData = ulc.AsUserLoginBills(response.Result),
                Code = LoginResponseCode.OK
            }, "Success");
        }

        /// <summary>
        //买家app登录（usertype=0,appsource=app），查询DeviceId在设备号库是否存在。
        //存在：
        //    走风控流程
        //不存在：
        //    查询设备号服务,是否是IOS10且已验证md5通过
        //         是：不走风控流程，设备加为常用设备
        //         否：继续走风控流程
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="sourceType"></param>
        /// <param name="deviceId"></param>
        /// <returns></returns>
        private bool iphone7UserLogin(string requestId, int userId, string sourceType, string deviceId)
        {
            bool isLogined = false;
            //买家app登录
            if (IsBuyerUser(userId, sourceType))
            {
                UserDeviceAuthEntity device = deviceRepo.FindUserDevice(userId, deviceId, UserDeviceAuthEntity.DeviceIdType);
                //不存在
                if (device == null)
                {
                    // 查询设备号服务,是否是IOS10且已验证md5通过
                    try
                    {
                        GetDeviceinfoResponse res = DeviceIdService.GetDeviceinfo(deviceId);

                        if (res != null && res.code == 0 && res.data.signVerified == 2 && res.data.osversion.StartsWith("ios10", StringComparison.InvariantCultureIgnoreCase))
                        {
                            YmatouLoggingService.Debug("iphone7UserLogin success, requestId: {0}, userId: {1}, deviceId: {2}", requestId, userId, deviceId);

                            //设备加为常用设备
                            device = new UserDeviceAuthEntity(userId, deviceId, sourceType, CONSTANT.UserDeviceAuth_DataSource_Iphone7, Convert.ToByte(UserDeviceAuthEntity.DeviceIdType));
                            device.SetDeviceAuthStatus(true);
                            deviceRepo.TrySaveChange(device);

                            isLogined = true;
                        }
                        else
                        {
                            YmatouLoggingService.Debug("iphone7UserLogin fail, requestId: {0}, userId: {1}, deviceId: {2}, msg: {3}", requestId, userId, deviceId, res == null ? "" : res.msg);
                        }
                    }
                    catch (Exception ex)
                    {
                        YmatouLoggingService.Error(string.Format("iphone7UserLogin error, err: {0}, requestId: {1}, userId: {2}, deviceId: {3}", ex.Message, requestId, userId, deviceId), ex);
                    }
                }
            }

            return isLogined;
        }

        private static bool CheckIpBlackList(string clientIp, string source)
        {
            //登录是否优先检查IP黑名单：Ip黑名单开启，app,pc风控策略为拒绝登录，该Ip是黑名单Ip
            var blackIpOpen = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                .TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("Open"), "1");
            if (blackIpOpen != "1") return false;

            return
                UserSafeVerify.GetBlackActionCode(source) == 400
                && BlackListAppService.TryCheckIpIsDisableRequest(clientIp, source);

        }
        public ResponseData<SignAuthResponseDto2> Logout(SSOTokenDto dto)
        {
            #region
            //手动清除 token 
            //var tokenInfo = new UserToken(dto.Token);      
            //TokenRepo.Remove(UserTokenSpecifications.MathEncryptionToken(dto.Token));
            #endregion
            return ResponseData<SignAuthResponseDto2>.CreateSuccess(new SignAuthResponseDto2 { Code = LoginResponseCode.OK }, "ok");
        }
        //IP区域认证
        public ResponseData<LoginAuthResponseDto2> AuthIpArea(IpAreaAuthStatusRequestDto dto, IRequest request = null)
        {

            if (request != null)
            {
                if (RequestUserService.isSendReadToJavaUserService(dto, "IpAreaAuthStatusRequestDto", "POST", new string[] { dto.LoginId }))
                {
                    var result = RequestUserService.sendReadRequest<ResponseData<LoginAuthResponseDto2>>(dto, "IpAreaAuthStatusRequestDto", request.RawUrl, "POST",
                        request.QueryString);
                    if (result.isRequestSuccess) return result.resp;
                }
            }

            YmtSystemAssert.AssertArgumentNotNull(dto, "dto cant't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.LoginId, "loginid can't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Password, "password cant't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Ip, "IP can't null");
            YmatouLoggingService.Debug("AuthIpArea, {0}", dto.ToString());

            var watch = Stopwatch.StartNew();
            //匹配用户名
            var loginResponse = UserLoginAndVerifyStatus(dto.LoginId, dto.Password);
            UserLoginBills userData = loginResponse.Result.UserData;
            int userId = userData != null ? userData.UserId : 0;

            if (!loginResponse.Success)
            {
                YmatouLoggingService.Debug("AuthIpArea {0} fail, IsIpAreaAuthSuccess: {1}, UserId: {2}, Rid: {3}, error: {4}", dto.LoginId, dto.Success, userId, dto.RequestId, loginResponse.LastErrorMessage);

                WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userId, "UserLoginAndVerifyStatus fail: " + loginResponse.LastErrorMessage + "; " + dto.ToString(), "AuthIpArea", AuthSafeHandleCode.IPAreaAuthUpdateFail);

                return loginResponse;
            }


            if (!dto.Success || (dto.VerifySecurityQuestion.HasValue && !dto.VerifySecurityQuestion.Value))
            {
                //TODO 客户端验证失败
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = userId,
                    AppSource = dto.Source,
                    AuthType = "ChangeIpAreaAuthStatus",
                    ResponseCode = AuthSafeHandleCode.ClientAuthFail,
                    LoginId = dto.LoginId,
                    LoginIp = dto.Ip,
                    Desc = "客户端身份验证失败",
                });
                YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 身份认证失败", userId, dto.Ip, dto.Source);
                return ResponseData<LoginAuthResponseDto2>.CreateFail(
                     new LoginAuthResponseDto2 { Code = LoginResponseCode.NeedIdentityVerify }, errorCode: "100",
                     lastErrorMessage: "请执行身份验证");
            }

            if (!dto.VerifySecurityQuestion.HasValue)
            {
                var dualSecurityRes = dualSecurityApp.GetDualSecurityResult(new GetDualSecurityResultRequestDto() { UserId = userId });

                if (dualSecurityRes.Success && dualSecurityRes.Result)
                {
                    //TODO 客户端验证失败
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = userId,
                        AppSource = dto.Source,
                        AuthType = "ChangeIpAreaAuthStatus",
                        ResponseCode = AuthSafeHandleCode.AppIpAreaClientNeedVerifySecurityQuestion,
                        LoginId = dto.LoginId,
                        LoginIp = dto.Ip,
                        Desc = "请执行安全问题验证",
                    });
                    YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 安全问题需要验证", userId, dto.Ip, dto.Source);
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(
                         new LoginAuthResponseDto2 { Code = LoginResponseCode.NeedIdentityVerify }, errorCode: "101",
                         lastErrorMessage: "请执行安全问题验证");
                }
            }

            //如果不是买手被禁止登录的IP则添加到常用Ip区域
            if (!CheckSellerDenyLogin(dto.Ip, userId, dto.Source))
            {
                //// 身份认证成功，将非买手用户的安全问题重试次数清零
                //if (dto.Success)
                //    securityQuestionAppService.ResetUserSecurityQuestionVerifyCount(userId);

                if (!ConfigUtility.UseDualSecurity && dto.Success)
                {
                    securityQuestionAppService.ResetUserSecurityQuestionVerifyCount(userId);
                }

                //添加IP区域认证记录
				var ipAreaAuthResponse = UserSafeVerify.ChangeIpAreaAuthStatus(loginResponse.Result.UserData.UserId,
					dto.LoginId, dto.Success, dto.Ip, dto.Source, IPAreaSourceType.AuthIpArea);
				//如果此认证过程异常，但客户端已经验证了用户短信，则返回token
				if (ipAreaAuthResponse.Success)
				{
					WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userData.UserId, "success" + "; " + dto.ToString(), "AuthIpArea", AuthSafeHandleCode.IPAreaAuthUpdateSuccess);
				}
				else
				{
					YmatouLoggingService.Debug("AuthIpArea ChangeIpAreaAuthStatus fail, loginid: {0}, IsIpAreaAuthSuccess: {1}, UserId: {2}, Rid: {3}, error: {4}", dto.LoginId, dto.Success, userId, dto.RequestId, ipAreaAuthResponse.LastErrorMessage);
					WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userId, "ChangeIpAreaAuthStatus fail: " + ipAreaAuthResponse.LastErrorMessage + "; " + dto.ToString(), "AuthIpArea", AuthSafeHandleCode.IPAreaAuthUpdateFail);

					if (!dto.Success)
					{
						return ipAreaAuthResponse;
					}
				}
            }
            else
            {
                WriteLoginLogAsync(new LoginAuthRequestDto()
                {
                    ClientIp = dto.Ip,LoginId = dto.LoginId,SourceType = dto.Source
                }, 0, 0, "买手本地局域网IP，已发送短信验证用户登录成功", AuthSafeHandleCode.SMSIpAuth, false, "SMSIpAuth");                
            }
            //创建token            
            var tokenResponse = tokenApp.CreateUserToken(new UserTokenDto
            {
                UserId = loginResponse.Result.UserData.UserId,
                TokenTimeOut = dto.TokenTimeOut,
                TokenSource = dto.TokenSource
            });
            //登录成功抛出消息
            if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["EnableLoginPostMessage"]) ||
                ConfigurationManager.AppSettings["EnableLoginPostMessage"] == "1")
            {
                MessageBusAgent.PublishAsync("ymatouuser", "login",
                   string.Format("{0}", userData.UserId), new
                        LoginMessageDto
                    {
                        UserId = userData.UserId,
                        Mobile = userData.LoginMobile,
                        LoginTime = DateTime.Now,
                    });
            }
            YmatouLoggingService.Debug("AuthIpArea finished, loginid: {0}, IsIpAreaAuthSuccess: {1}, UserId: {2}, Rid: {3}", dto.LoginId, dto.Success, userId, dto.RequestId);

            loginResponse.Result.UserData.Sign = tokenResponse.Result;

            return ResponseData<LoginAuthResponseDto2>.CreateSuccess(new LoginAuthResponseDto2
            {
                UserData = userData,
                Code = LoginResponseCode.OK
            });                                                         
        }

        //设备认证
        public ResponseData<LoginAuthResponseDto2> AuthDevice(DeviceAuthStatusRequestDto dto, IRequest request = null)
        {

            if (request != null)
            {
                if (RequestUserService.isSendReadToJavaUserService(dto, "DeviceAuthStatusRequestDto", "POST", new string[] { dto.LoginId }))
                {
                    var result = RequestUserService.sendReadRequest<ResponseData<LoginAuthResponseDto2>>(dto, "DeviceAuthStatusRequestDto", request.RawUrl, "POST",
                        request.QueryString);
                    if (result.isRequestSuccess) return result.resp;
                }
            }

            YmtSystemAssert.AssertArgumentNotNull(dto, "dto cant't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.LoginId, "loginid can't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Password, "password cant't null");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Ip, "IP can't null");
            YmtSystemAssert.AssertArgumentFalse(dto.Source.ToLower() == "app" && dto.DeviceId.IsEmpty() && dto.ClientId.IsEmpty(), "DeviceId clientId cant'null");

            YmatouLoggingService.Debug("AuthDevice, {0}", dto.ToString());

            var watch = Stopwatch.StartNew();

            //非APP请求此接口，则返回错误
            if (!YmtAuthSafeConstant.IsValidSourceForAuthDevice(dto.Source))
            {
                YmatouLoggingService.Debug("AuthDevice {0}, error Source: {1}", dto.LoginId, dto.Source);
                WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, 0, "error Source: " + dto.Source + "; " + dto.ToString(), "AuthDevice", AuthSafeHandleCode.DeviceAuthUpdateFail);
                
                return ResponseData<LoginAuthResponseDto2>.CreateFail(new LoginAuthResponseDto2 { }, errorCode: "400", lastErrorMessage: "错误的请求参数, Source: " + dto.Source);
            }
            //匹配用户名
            var loginResponse = UserLoginAndVerifyStatus(dto.LoginId, dto.Password);
            UserLoginBills userData = loginResponse.Result.UserData;
            int userId = userData != null ? userData.UserId : 0;

            if (!loginResponse.Success)
            {
                YmatouLoggingService.Debug("AuthDevice {0}, UserLoginAndVerifyStatus fail, IsDeviceAuthSuccess: {1}, UserId: {2}, Rid: {3}, error: {4}", dto.LoginId, dto.Success, userId, dto.RequestId, loginResponse.LastErrorMessage);

                WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userId, "UserLoginAndVerifyStatus fail: " + loginResponse.LastErrorMessage + "; " + dto.ToString(), "AuthDevice", AuthSafeHandleCode.DeviceAuthUpdateFail);

                return loginResponse;
            }

            if (!dto.Success || (dto.VerifySecurityQuestion.HasValue && !dto.VerifySecurityQuestion.Value))
            {
                //TODO 客户端验证失败
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = userId,
                    AppSource = dto.Source,
                    AuthType = "ChangeDeviceAuthStatus",
                    ResponseCode = AuthSafeHandleCode.AppDeviceClientVerifyFail,
                    LoginId = dto.LoginId,
                    LoginIp = dto.Ip,
                    Desc = "客户端身份验证失败",
                });

                YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 身份认证失败", userId, dto.Ip, dto.Source);
                return ResponseData<LoginAuthResponseDto2>.CreateFail(
                     new LoginAuthResponseDto2 { Code = LoginResponseCode.NeedIdentityVerify }, errorCode: "100",
                     lastErrorMessage: "请执行身份验证");
            }

            if (!dto.VerifySecurityQuestion.HasValue)
            {
                var dualSecurityRes = dualSecurityApp.GetDualSecurityResult(new GetDualSecurityResultRequestDto() { UserId = userId });

                if (dualSecurityRes.Success && dualSecurityRes.Result)
                {
                    //TODO 客户端验证失败
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = userId,
                        AppSource = dto.Source,
                        AuthType = "ChangeDeviceAuthStatus",
                        ResponseCode = AuthSafeHandleCode.AppDeviceClientNeedVerifySecurityQuestion,
                        LoginId = dto.LoginId,
                        LoginIp = dto.Ip,
                        Desc = "请执行安全问题验证",
                    });
                    YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 安全问题需要验证", userId, dto.Ip, dto.Source);
                    return ResponseData<LoginAuthResponseDto2>.CreateFail(
                         new LoginAuthResponseDto2 { Code = LoginResponseCode.NeedIdentityVerify }, errorCode: "101",
                         lastErrorMessage: "请执行安全问题验证");
                }
            }

            var ipAuthResult = ResponseData<LoginAuthResponseDto2>.CreateSuccess(
                new LoginAuthResponseDto2 { Code = LoginResponseCode.OK });
            //如果不是买手被禁止登录的IP则添加到常用Ip区域
            if (!CheckSellerDenyLogin(dto.Ip, userId, dto.Source))
            {
                ipAuthResult = UserSafeVerify.ChangeIpAreaAuthStatus(userId,
                    dto.LoginId, dto.Success, dto.Ip, dto.Source, IPAreaSourceType.AuthDevice);
            }
            else
            {
                WriteLoginLogAsync(new LoginAuthRequestDto()
                {
                    ClientIp = dto.Ip,
                    LoginId = dto.LoginId,
                    SourceType = dto.Source
                }, 0, 0, "买手本地局域网IP，已发送短信验证用户登录成功", AuthSafeHandleCode.SMSIpAuth, false, "SMSIpAuth");
            }

            //YmatouLoggingService.Debug("Rid: {0}, ChangeIpAreaAuthStatus isSuccess: {1}, ErrorCode: {2}, {3}", dto.RequestId, ipAuthResult.Success, ipAuthResult.ErrorCode, ipAuthResult.LastErrorMessage);

            ResponseData<LoginAuthResponseDto2> authResult = null;

            //非买手的才更新设备状态
            if (!IsSellerUser(userId, dto.Source))
            {
                //// 身份认证成功，将非买手用户的安全问题重试次数清零
                //if (dto.Success)
                //    securityQuestionAppService.ResetUserSecurityQuestionVerifyCount(userId);

                if (!ConfigUtility.UseDualSecurity && dto.Success)
                {
                    securityQuestionAppService.ResetUserSecurityQuestionVerifyCount(userId);
                }
                
                authResult = UserSafeVerify.ChangeDeviceAuthStatus(loginResponse.Result.UserData.UserId,
                loginResponse.Result.UserData.LoginId, dto.Success, dto.DeviceId, dto.ClientId, dto.Ip, dto.Source, CONSTANT.UserDeviceAuth_DataSource_AuthDevice);
                //YmatouLoggingService.Debug("Rid: {0}, ChangeDeviceAuthStatus isSuccess: {1}, ErrorCode: {2}, {3}", dto.RequestId, authResult.Success, authResult.ErrorCode, authResult.LastErrorMessage);

                //如果此认证客户端IP区域，设备都认证失败，但客户端已经验证了用户短信，则返回token
                if ((!authResult.Success && !ipAuthResult.Success) && !dto.Success)
                {
                    YmatouLoggingService.Error("AuthDevice {0} fail, IsDeviceAuthSuccess: {1}, UserId: {2}, Rid: {3}, 设备号和IP区域认证失败", dto.LoginId, dto.Success, userId, dto.RequestId);

                    return authResult;
                }

                if (authResult.Success)
                {
                    WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userData.UserId, "success" + "; " + dto.ToString(), "AuthDevice", AuthSafeHandleCode.DeviceAuthUpdateSuccess);
                }
                else
                {
                    WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds, userId, "设备认证更新失败;" + dto.ToString(), "AuthDevice", AuthSafeHandleCode.DeviceAuthUpdateFail);
                }

                YmatouLoggingService.Debug("AuthDevice finished, loginid: {0}, IsDeviceAuthSuccess: {1}, UserId: {2}, Rid: {3}", dto.LoginId, dto.Success, userId, dto.RequestId);
            }
            //登录成功抛出消息
            if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["EnableLoginPostMessage"]) ||
                ConfigurationManager.AppSettings["EnableLoginPostMessage"] == "1")
            {
                MessageBusAgent.PublishAsync("ymatouuser", "login",
                   string.Format("{0}", userData.UserId), new
                        LoginMessageDto
                    {
                        UserId = userData.UserId,
                        Mobile = userData.LoginMobile,
                        LoginTime = DateTime.Now,
                        DeviceId = dto.DeviceId,
                    });
            }
            var tokenResponse = tokenApp.CreateUserToken(new UserTokenDto
            {
                UserId = loginResponse.Result.UserData.UserId,
                TokenTimeOut = dto.TokenTimeOut,
                TokenSource = dto.TokenSource
            });

            loginResponse.Result.UserData.Sign = tokenResponse.Result;

            ///更新设备号信息
            DeviceIdService.UpdateDeviceinfo(userId, dto.DeviceId);

            return ResponseData<LoginAuthResponseDto2>.CreateSuccess(new LoginAuthResponseDto2
            {
                UserData = userData,
                Code = LoginResponseCode.OK
            });           
        }
       
        public ResponseData<LoginAuthResponseDto2> InternalServiceAuth(LoginAuthRequestDto dto)
        {
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的LoginAuthRequestDto");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.LoginId, "无效的LoginId");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.Pas, "无效的Pas");
            YmtSystemAssert.AssertArgumentNotEmpty(dto.ClientIp, "无效客户端IP");
            var watch = Stopwatch.StartNew();
            var loginResult = UserLoginAndVerifyStatus(dto.LoginId, dto.Pas);
            watch.Stop();
            WriteLoginLogAsync(dto, watch.Elapsed.TotalMilliseconds,
                loginResult.Success ? loginResult.Result.UserData.UserId : 0, "InternalServiceAuth", -1, false);
            return loginResult;
        }

        private ResponseData<LoginAuthResponseDto2> UserLoginAndVerifyStatus(string loginId, string password,
            bool setToken = false)
        {
            if (loginId.IsEmpty() || password.IsEmpty())
            {
                return ResponseData<LoginAuthResponseDto2>.CreateFail(
                new LoginAuthResponseDto2
                {
                    Code = LoginResponseCode.LoingIdOrPasError
                }, lastErrorMessage: "用户名和密码不能为空");
            }
            var query = UserLoginAuthSpecifications.MatchLoginKey(loginId);
            var ulc = UlcRepo.FindOne(query, true);
            if (ulc == null)
            {
                return ResponseData<LoginAuthResponseDto2>.CreateFail(
                new LoginAuthResponseDto2
                {
                    Code = LoginResponseCode.LoingIdOrPasError
                }, lastErrorMessage: "用户名或密码错误");
            }
            //检查密码，及用户状态
            return ulc.CheckUserAllowLogin(password, loginId);
        }

        private bool CheckSellerDenyLogin(string ip, int userId, string source)
        {
            //根据来源检查 是买手被禁用登录的IP，且用户类型为买手
            var isSellerSource = YmatouBlacklistUserSpecifications.IsSellerSource(source);
            var denyLogin = CheckSellerSourceDenyLogin(ip);
            if (isSellerSource && denyLogin) return true;
            var userType= UserTypeCache.GetOrAdd(userId, uid => new LocalCache2<int, byte>._CacheItem
            {
                Value = Convert.ToByte(RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
                ExpiredTime = TimeSpan.FromDays(1)
            });
            return !isSellerSource && denyLogin && userType == 1;
        }
     
        private bool CheckSellerSourceDenyLogin(string ip)
        {            
            var sellerDenyLogin = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
                .TryGet(SafeVerifyConfiguration.BlackListCfg.IpBlackListKey("SellerDenyLogin"), "lan");
            if (string.IsNullOrEmpty(sellerDenyLogin) || string.IsNullOrWhiteSpace(sellerDenyLogin))
            {
                return false;
            }
            else
            {
                var denyLoginArray = sellerDenyLogin.Split(new char[] {';'});              
                var ipArea = UserSafeVerify.FindUserIpArea(ip);
                if (ipArea == null)
                {
                    YmatouLoggingService.Error("IP {0} 原始库未查找到数据，需要验证身份",ip);
                    return true;
                }
                var country = ipArea.Country.ToLower();
                return denyLoginArray.Any(l => l == country);
            }        
        }

        private bool IsCheckLoginSafe(string appSource, int userId, string terminalSource,int appTypeChannel)
        {
            //APP来源，IOS，android 老的通道不走安全检查流程，新的通道根据配置决定是否走安全检查流程
            if (appSource.ToLower() == "app" || appSource.ToLower() == "1")
            {
                if (!string.IsNullOrWhiteSpace(terminalSource) && terminalSource.ToLower() == "ios")
                {
                     if (appTypeChannel == 0) return false;
                  
                }
                if (!string.IsNullOrWhiteSpace(terminalSource) && terminalSource.ToLower() == "android")
                {
                     if (appTypeChannel == 0) return false;
                    
                }
            }
            var checkList =
                SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration().TryGet(SafeCfgKey.LoginSafe_Check_List);
            if (checkList.IsEmpty() || checkList == "*") return true;
            var lower = appSource.ToLower();
            var checkSpecArray = checkList.Split(new char[] {','});
            var isCheck = checkSpecArray.Any(c => c == lower);
            YmatouLoggingService.Debug("[IsCheckLoginSafe] AppSource {0} IsCheckLoginSafe {1}", appSource, isCheck);
            if (isCheck) return true;
            if (!checkSpecArray.Any(e => e.StartsWith("check_usertype")))
                return false;
            var userType = UserTypeCache.GetOrAdd(userId, uid => new LocalCache2<int, byte>._CacheItem
            {
                Value =Convert.ToByte( RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
                ExpiredTime = TimeSpan.FromDays(1)
            });            
            if (userType == byte.MinValue)
            {
                YmatouLoggingService.Debug("TryFindUserType error ,userid:{0},appSource:{1}", userId, appSource);
                return false;
            }
            if (checkSpecArray.Any(e=>e==string.Format("check_usertype_{0}",userType.ToString())))
            {
                YmatouLoggingService.Debug("[IsCheckLoginSafe] AppSource {0} IsCheckLoginSafe {1},userid:{2}", appSource, isCheck,
                    userId);
                return true;
            }
            return false;
        }


        /// <summary>
        /// 检查用户是否是买手
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        private bool IsSellerUser(int userId, string source)
        {
            bool returnVal = false;
            string msg = string.Empty;
            bool isSellerSource = YmatouBlacklistUserSpecifications.IsSellerSource(source);
            if (isSellerSource)
            {
                int userType = -1;
                if (userId > 0)
                {
                    // var userType = RepositoryDependencies.UserRepository.Invoke().TryFindUserType(userId);

                    userType = (int)UserTypeCache.GetOrAdd(userId, uid => new LocalCache2<int, byte>._CacheItem
                    {
                        Value = Convert.ToByte(RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
                        ExpiredTime = TimeSpan.FromDays(1)
                    });   
                    
                    // userType 为1表示买手
                    if (userType == 1)
                    {
                        returnVal = true;
                    }
                }

                msg = string.Format("IsSellerUser, userid: {0}, userType: {1}, source: {2}, isSellerSource: {3}, IsSellerUser: {4}", userId, userType, source, isSellerSource, returnVal);
            }
            else
            {
                msg = string.Format("IsSellerUser, userid: {0}, source: {1}, isSellerSource: {2}", userId, source, isSellerSource);                
            }

            YmatouLoggingService.Debug(msg);
            
            return returnVal;
        }

        ///// <summary>
        ///// 检查用户是否是需要记录“在线状态”的买手
        ///// </summary>
        ///// <param name="userId"></param>
        ///// <param name="source"></param>
        ///// <returns></returns>
        //private bool IsSellerOnlineUser(int userId, string source)
        //{
        //    bool returnVal = false;
        //    string msg = string.Empty;
        //    bool isSellerOnlineSource = YmatouBlacklistUserSpecifications.IsSellerOnlineSource(source);
        //    if (isSellerOnlineSource)
        //    {
        //        int userType = -1;
        //        if (userId > 0)
        //        {
        //            // var userType = RepositoryDependencies.UserRepository.Invoke().TryFindUserType(userId);

        //            userType = (int)UserTypeCache.GetOrAdd(userId, uid => new LocalCache2<int, byte>._CacheItem
        //            {
        //                Value = Convert.ToByte(RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
        //                ExpiredTime = TimeSpan.FromDays(1)
        //            });

        //            // userType 为1表示买手
        //            if (userType == 1)
        //            {
        //                returnVal = true;
        //            }
        //        }
        //    }

        //    return returnVal;
        //}

        /// <summary>
        /// 检查用户是否是买家
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        private bool IsBuyerUser(int userId, string source)
        {
            bool returnVal = false;

            if (string.Equals("app", source, StringComparison.InvariantCultureIgnoreCase))
            {
                if (userId > 0)
                {
                    var type= UserTypeCache.GetOrAdd(userId, uid => new LocalCache2<int, byte>._CacheItem
                    {
                        Value = Convert.ToByte( RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
                        ExpiredTime = TimeSpan.FromDays(1)
                    });                   
                    // userType 为0表示买家
                    if (type == 0)
                    {
                        returnVal = true;
                    }
                }
            }
            return returnVal;
        }

        /// <summary>
        /// 记录IP区域认证登录日志（PC）
        /// </summary>
        /// <param name="dto"></param>
        /// <param name="runTime"></param>
        /// <param name="userid"></param>
        /// <param name="desc"></param>
        /// <param name="authType"></param>
        /// <param name="code"></param>
        /// <param name="toMongo"></param>
        private void WriteLoginLogAsync(IpAreaAuthStatusRequestDto dto, double runTime, int userid, string desc, string authType
            , int code = 0, bool toMongo = true)
        {
            //mongodb 增量日志
            var loginlog = ConfigurationManager.AppSettings["EnableMongoLogStore"];
            if ((string.IsNullOrEmpty(loginlog) || loginlog == "1") && toMongo)
            {
                LoginHistoryRepo.LogAsync(new YmtUserLoginEvent(dto.LoginId
                    , userid
                    , dto.Ip
                    , desc
                    , dto.Source
                    , runTime
                    , responseCode: code));
            }
            //SQL数据库全量日志
            var sqlLogStore = ConfigurationManager.AppSettings["EnableSqlLogStore"];
            if (string.IsNullOrEmpty(sqlLogStore) || sqlLogStore == "1")
            {
                if (!"success".Equals(desc))
                {
                    desc = string.Format("{0}, {1}", desc, dto.ToString());
                }

                LogTask.Instance.EnQueue(new UserLoginLogHistory
                {
                    RequestId = dto.RequestId,
                    UserId = userid,
                    LoginId = dto.LoginId,
                    LoginIp = dto.Ip,
                    ClientType = dto.Source,
                    TerminalType = "Pc",
                    RunTime = runTime,
                    AuthType = authType,
                    ResponseCode = code,
                    Desc = desc
                });
            }
            //用户登录计数器日志
            LoginCountLog(dto.LoginId, code);
        }

        /// <summary>
        /// 记录设备认证登录日志（App）
        /// </summary>
        /// <param name="dto"></param>
        /// <param name="runTime"></param>
        /// <param name="userid"></param>
        /// <param name="desc"></param>
        /// <param name="authType"></param>
        /// <param name="code"></param>
        /// <param name="toMongo"></param>
        private void WriteLoginLogAsync(DeviceAuthStatusRequestDto dto, double runTime, int userid, string desc
            , string authType, int code = 0, bool toMongo = true)
        {
            //mongodb 增量日志
            var loginlog = ConfigurationManager.AppSettings["EnableMongoLogStore"];
            if ((string.IsNullOrEmpty(loginlog) || loginlog == "1") && toMongo)
            {
                LoginHistoryRepo.LogAsync(new YmtUserLoginEvent(dto.LoginId
                    , userid
                    , dto.Ip
                    , desc
                    , dto.Source
                    , runTime
                    , deviceid: dto.DeviceId
                    , clientId: dto.ClientId
                    , responseCode: code));
            }
            //SQL数据库全量日志
            var sqlLogStore = ConfigurationManager.AppSettings["EnableSqlLogStore"];
            if (string.IsNullOrEmpty(sqlLogStore) || sqlLogStore == "1")
            {
                LogTask.Instance.EnQueue(new UserLoginLogHistory
                {
                    RequestId = dto.RequestId,
                    UserId = userid,
                    LoginId = dto.LoginId,
                    LoginIp = dto.Ip,
                    ClientType = dto.Source,
                    RunTime = runTime,
                    AuthType = authType,
                    DeviceId = dto.DeviceId,
                    ClientId = dto.ClientId,
                    ResponseCode = code,
                    Desc = desc
                });
            }
            //用户登录计数器日志
            LoginCountLog(dto.LoginId, code);
        }

        private void WriteLoginLogAsync(LoginAuthRequestDto dto, double runTime, int userid, string desc, int code = 0, bool toMongo = true, string authType = "success")
        {
            //mongodb 增量日志
            var loginlog = ConfigurationManager.AppSettings["EnableMongoLogStore"];
            if ((string.IsNullOrEmpty(loginlog) || loginlog == "1") && toMongo)
            {
                LoginHistoryRepo.LogAsync(new YmtUserLoginEvent(dto.LoginId
                    , userid
                    , dto.ClientIp
                    , dto.Description
                    , dto.SourceType
                    , runTime
                    , tSource: dto.TSource
                    , description: desc
                    , deviceid: dto.DeviceId
                    , clientId: dto.ClientId
                    , responseCode: code));
            }
            //SQL数据库全量日志
            var sqlLogStore = ConfigurationManager.AppSettings["EnableSqlLogStore"];
            if (string.IsNullOrEmpty(sqlLogStore) || sqlLogStore == "1")
            {
                if (!"success".Equals(authType))
                {
                    desc = string.Format("{0},  {1}", desc, dto.ToString());
                }

                LogTask.Instance.EnQueue(new UserLoginLogHistory
                {
                    RequestId = HttpHelper.GetHeaderParameter(YmtUserKey.RequestId),
                    UserId = userid,
                    LoginId = dto.LoginId,                    
                    LoginIp = dto.ClientIp,
                    ClientType = dto.SourceType,
                    TerminalType = dto.TSource,
                    DeviceId = dto.DeviceId,
                    ClientId = dto.ClientId,
                    RunTime = runTime,
                    AuthType = authType,
                    ResponseCode = code,
                    Desc = desc                   
                });
            }
            //用户登录计数器日志
            LoginCountLog(dto.LoginId, code);

            //用户登录记录设备登录计数
            LoginDeviceCountLog(dto.DeviceId, dto.ClientId, code == AuthSafeHandleCode.LoginSuccess);
        }

        private void LoginCountLog(string loginId, int code)
        {
            //计数器日志记录所有登录次数
            if (ConfigurationManager.AppSettings["CounterService_W_LoginCount"].IsEmpty() ||
                ConfigurationManager.AppSettings["CounterService_W_LoginCount"] == "1")
            {
                Task.Factory.StartNew(() =>
                {
                    var result = NaturalCounterClientAgent.IncreaseNatualCounter(ConfigurationManager.AppSettings["AppId"],
                        LoginEventSpecifications.AUTHSERVICE_KEY, LoginEventSpecifications.GenerateLoginKey(loginId));
                    YmatouLoggingService.Debug("request counter service count logininfo ok,{0},count {1}", loginId,result);
                })
                    .ContinueWith(
                        task =>
                            YmatouLoggingService.Error(
                                "request counter service count logininfo error,loginId {0} ex {1}",
                                loginId,
                                task.Exception.ToString()),
                        TaskContinuationOptions.OnlyOnFaulted);
            }
            //记录登录失败的次数
            if ( (ConfigurationManager.AppSettings["CounterService_W_LoginCount"].IsEmpty() ||
                                 ConfigurationManager.AppSettings["CounterService_W_LoginCount"] == "1"))
            {
                Task.Factory.StartNew(() =>
                {
                    var result= NaturalCounterClientAgent.IncreaseNatualCounter(ConfigurationManager.AppSettings["AppId"],
                        LoginEventSpecifications.AUTHSERVICE_KEY,
                        LoginEventSpecifications.GenerateLoginSpecKey(loginId,code));
                    YmatouLoggingService.Debug("login fail count request counter service count ok,{0},count,{1}", loginId,result);
                }).ContinueWith(
                    task =>
                        YmatouLoggingService.Error(
                            "request counter service  count login fail error,loginId {0} ex {1}", loginId,
                            task.Exception.ToString()),
                    TaskContinuationOptions.OnlyOnFaulted);
            }
        }

        /// <summary>
        /// 记录设备登录计数日志
        /// </summary>
        /// <param name="deviceType"></param>
        /// <param name="deviceId"></param>
        /// <param name="success"></param>
        private void LoginDeviceCountLog(string deviceId, string clientId, bool success)
        {
            //计数器日志记录所有设备登录日志
            string key = string.Empty;
            byte deviceType = UserDeviceAuthEntity.DeviceIdType;
            if (deviceId.IsEmpty())
            {
                key = clientId;
                deviceType = UserDeviceAuthEntity.ClientIdType;
            }
            else
            {
                key = deviceId;
            }

            if (key.IsEmpty())
            {
                YmatouLoggingService.Debug("设备号 ，client ID 都为空无法添加设备登录计数日志");
                return;
            }

            string targetId = LoginEventSpecifications.GenerateLoginDeviceSpecKey(deviceType, deviceId, success);
            string counter = success ? LoginEventSpecifications.AUTHSERVICE_LOGIN_DEVICE_SUCCESS_KEY : LoginEventSpecifications.AUTHSERVICE_LOGIN_DEVICE_FAIL_KEY;


            Task.Factory.StartNew(() =>
            {
                var result = NaturalCounterClientAgent.IncreaseNatualCounter(ConfigurationManager.AppSettings["AppId"], counter, targetId);
                YmatouLoggingService.Debug("request counter service count logindevice ok, targetId: {0}, count {1}", targetId, result);
            })
            .ContinueWith(
                        task =>
                            YmatouLoggingService.Error(
                                "request counter service count logindevice error, targetId: {0}, ex {1}",
                                targetId,
                                task.Exception.ToString()),
                        TaskContinuationOptions.OnlyOnFaulted);
        }

        private bool CheckUserLoginCountGTLimit(string loginKey)
        {
            var loginIdLimitCheck =
                SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration().TryGet(SafeCfgKey.LoginSafe_LoginIdLimit_CheckSpec);
            if (loginIdLimitCheck == null) return false;

            var splitCfg = loginIdLimitCheck.Split(new char[] { ',' });
            if (splitCfg == null || splitCfg.Length != 2)
            {
                YmatouLoggingService.Debug("最近N分钟登录密码错误次数超过M次 配置错误");
                return false;
            }

            YmatouLoggingService.Debug("获取“最近N分钟登录密码错误次数超过M次”（LoginSafe_LoginIdLimit_CheckSpec）配置: " + loginIdLimitCheck);

            int LoginCountLimitMinutes = splitCfg[0].ConvertToInt32(1);
            int LoginLimit = splitCfg[1].ConvertToInt32(30);            
            
            if (LoginLimit == -1) return false;

            //如果关闭请求计数器服务，则查询mongodb
            //if (ConfigurationManager.AppSettings["CounterService_R_LoginCount"].IsEmpty()
            //    || ConfigurationManager.AppSettings["CounterService_R_LoginCount"] == "0")
            //    return LoginHistoryRepo.Count(
            //        LoginEventSpecifications.MatchLoginCount(loginKey, new TimeSpan(0, LoginCountLimitMinutes, 0)),
            //        LoginEventSpecifications.LoginLogDb, LoginEventSpecifications.LoginLogTb()) >=
            //           Convert.ToInt32(LoginLimit);          
            //从计数器服务获取登录次数           
            var count = LoginHistoryRepo.GetUserLoginCount(LoginEventSpecifications.AUTHSERVICE_KEY,
                LoginEventSpecifications.GenerateLoginKey(loginKey),
                DateTime.Now.Subtract(new TimeSpan(0, LoginCountLimitMinutes, 0)), DateTime.Now);
            if (count > -1)
                return count >= Convert.ToInt32(LoginLimit);
            YmatouLoggingService.Debug("请求计数器服务失败,统计mongodb数据");
            //如果计数器服务请求异常，则获取mongodb日志          
            var limit =
                LoginHistoryRepo.Count(
                    LoginEventSpecifications.MatchLoginCount(loginKey, new TimeSpan(0, LoginCountLimitMinutes, 0)),
                    LoginEventSpecifications.LoginLogDb, LoginEventSpecifications.LoginLogTb());
            return limit >= Convert.ToInt32(LoginLimit);
        }

        private void UpdateLoginTimeAsync(LoginAuthRequestDto dto, UserLoginBill ulc)
        {
            //来源：userservice.ymatou.com，则不更新登录时间
            if (dto.SourceType != innerClientSource
                && ConfigurationManager.AppSettings["UpdateLoginTimeEnable"] == "1"
                && ulc != null)
            {
                Task.Factory.StartNew(() =>
                {
                    var stopWatch = Stopwatch.StartNew();
                    LoginHistoryRepo.UpdateLoginTimeAsync(ulc.UserId);
                    stopWatch.Stop();
                    if (stopWatch.ElapsedMilliseconds > 1000)
                        YmatouLoggingService.Debug(string.Format("UpdateLoginTimeAsync run time {0:N0} ms"), stopWatch.ElapsedMilliseconds);
                }).ContinueWith(t => YmatouLoggingService.Error("更新用户登录时间异常", t.Exception), TaskContinuationOptions.OnlyOnFaulted);
            }
        }       
        private void BlacklistUserEvent(LoginAuthRequestDto dto, bool isAdd = false)
        {
            if (YmatouBlacklistUser.Enable)
                EventBus.Instance.Publish(new BlackUserEvent(dto.LoginId, dto.ClientIp, isAdd));
        }
    }
}
